using Cysharp.Threading.Tasks;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using System;
using System.Collections.Generic;
using UnityEngine;

namespace IQIGame.Onigao.GamePlay
{
    public class LevelPlayEffectCtrl : LevelPlaySubBaseCtrl
    {
        private struct EntityEffectId : IEquatable<EntityEffectId>
        {
            public int entityId;
            public int effectResId;

            public EntityEffectId(int entityId, int effectResId)
            {
                this.entityId = entityId;
                this.effectResId = effectResId;
            }

            public bool Equals(EntityEffectId other)
            {
                return this.entityId == other.entityId && this.effectResId == other.effectResId;
            }
        }

        private LevelPlaySceneEffectCtrl _sceneEffectCtrl;
        private LevelPlayEntityEffectCtrl _entityEffectCtrl;
        /// <summary>
        /// id索引
        /// </summary>
        private Dictionary<int, LevelSceneEffectView> _effectMapping = new Dictionary<int, LevelSceneEffectView>();
        private Dictionary<EntityEffectId, Entity> _entityEffectMap = new Dictionary<EntityEffectId, Entity>();

        public LevelPlayEffectCtrl()
        {
            _sceneEffectCtrl = AddSubController<LevelPlaySceneEffectCtrl>();
            _entityEffectCtrl = AddSubController<LevelPlayEntityEffectCtrl>();
        }

        protected override void OnModuleGameplayInit()
        {

        }

        protected override void OnSceneLoaded()
        {
            MsgDispatcher.AddListener<int, List<int>, int, int, bool>(MsgEventType.Level_NotifyChangeBindEffect, OnChangeEntityBindEffect);
            MsgDispatcher.AddListener<int>(MsgEventType.Level_EntityStartHide, OnEntityStartHide);
        }

        /// <summary>
        /// 播放实体挂载特效
        /// </summary>
        /// <param name="entity">实体对象</param>
        /// <param name="bindPointId">挂点id</param>
        /// <param name="resId">特效资源id</param>
        /// <param name="offset">特效相对于挂点的位置偏移</param>
        /// <returns></returns>
        public async UniTask<Entity> ShowEntityEffect(LevelPlayBaseEntity entity, EntityBindPointCommonType bindPointId, int resId, Vector3 offset)
        {
            var key = new EntityEffectId(entity.id, resId);
            if (_entityEffectMap.ContainsKey(key))
            {
                LogGame.LogError($"实体 [{entity.GetDynLevelEntity().levelEntityConfig.id}] 上已存在res id为[{resId}]的挂点。");
                return null;
            }
            _entityEffectMap.Add(key, null);
            var effectEntity = await _entityEffectCtrl.ShowEntity(entity, bindPointId, resId, offset);
            if (effectEntity != null)
            {
                _entityEffectMap[key] = effectEntity;
            }
            return effectEntity;
        }

        /// <summary>
        /// 播放实体挂载特效
        /// </summary>
        /// <param name="entity">实体对象</param>
        /// <param name="bindPointId">挂点id</param>
        /// <param name="resId">特效资源id</param>
        /// <returns></returns>
        public UniTask<Entity> ShowEntityEffect(LevelPlayBaseEntity entity, EntityBindPointCommonType bindPointId, int resId)
        {
            return ShowEntityEffect(entity, bindPointId, resId, Vector3.zero);
        }

        /// <summary>
        /// 播放一次性实体挂载特效
        /// 播放实体挂载特效，并于一定时间后停止
        /// </summary>
        /// <param name="entity">实体对象</param>
        /// <param name="bindPointId">挂点id</param>
        /// <param name="resId">特效资源id</param>
        /// <param name="offset">特效相对于挂点的位置偏移</param>
        /// <param name="duration">播放持续时间（秒）</param>
        /// <returns></returns>
        public async UniTask ShowEntityEffect(LevelPlayBaseEntity entity, EntityBindPointCommonType bindPointId, int resId, Vector3 offset, float duration)
        {
            int entityId = entity.id;
            await ShowEntityEffect(entity, bindPointId, resId, offset);
            await UniTask.Delay((int)(duration * 1000));
            HideEntityEffect(entityId, resId);
        }

        /// <summary>
        /// 播放一次性实体挂载特效
        /// 播放实体挂载特效，并于一定时间后停止
        /// </summary>
        /// <param name="entity">实体对象</param>
        /// <param name="bindPointId">挂点id</param>
        /// <param name="resId">特效资源id</param>
        /// <param name="duration">播放持续时间（秒）</param>
        /// <returns></returns>
        public UniTask ShowEntityEffect(LevelPlayBaseEntity entity, EntityBindPointCommonType bindPointId, int resId, float duration)
        {
            return ShowEntityEffect(entity, bindPointId, resId, Vector3.zero, duration);
        }

        public async UniTask<Entity> ShowSceneEffect(int sceneEffectId, Transform effectRoot, Vector3 offset = default)
        {
            if (_effectMapping.ContainsKey(sceneEffectId))
            {
                LogGame.LogWarning("[LevelEffectHoldComponent|AddEffectByScene] AddSceneEffct failed, id:" + sceneEffectId);
                return null;
            }
            var sceneEffectCfg = TableCenter.levelSceneEffect.Get(sceneEffectId);
            string path = PathConstant.GetEffectPrefabPath(sceneEffectCfg.EffectRes);
            var effect = await _sceneEffectCtrl.ShowEntity(effectRoot, path, sceneEffectCfg.Position.ToVector3(GameGlobalConst.LogicPosScale) + offset);
            _effectMapping.Add(sceneEffectCfg.Id, effect as LevelSceneEffectView);
            return effect;
        }

        /// <summary>
        /// 隐藏实体挂载特效（纯表现）
        /// </summary>
        /// <param name="entity">实体对象</param>
        /// <param name="resId">特效资源id</param>
        public void HideEntityEffect(LevelPlayBaseEntity entity, int resId)
        {
            HideEntityEffect(entity.id, resId);
        }

        /// <summary>
        /// 隐藏实体挂载特效（纯表现）
        /// </summary>
        /// <param name="entityId">实体id</param>
        /// <param name="resId">特效资源id</param>
        public void HideEntityEffect(int entityId, int resId)
        {
            var key = new EntityEffectId(entityId, resId);
            if (_entityEffectMap.TryGetValue(key, out var effectEntity))
            {
                _entityEffectCtrl.HideEntity(effectEntity);
                _entityEffectMap.Remove(key);
            }
        }

        /// <summary>
        /// 隐藏实体所有的挂载特效
        /// </summary>
        /// <param name="entityId"></param>
        private void HideEntityEffect(int entityId)
        {
            var keys = ListPool<EntityEffectId>.Get();
            foreach (var key in _entityEffectMap.Keys)
            {
                if (key.entityId == entityId)
                {
                    keys.Add(key);
                }
            }
            foreach (var key in keys)
            {
                if (_entityEffectMap.TryGetValue(key, out var effectEntity))
                {
                    _entityEffectCtrl.HideEntity(effectEntity);
                    _entityEffectMap.Remove(key);
                }
            }
        }

        public void HideFreeEffect(Entity entity)
        {
            _entityEffectCtrl.HideEntity(entity);
        }

        public void HideSceneEffect(int cid)
        {
            if (_effectMapping.TryGetValue(cid, out var effect))
            {
                _sceneEffectCtrl.HideEntity(effect);
                _effectMapping.Remove(cid);
            }
        }

        /// <summary>
        /// 既不是区域特效/全局场景特效，又不是实体挂点特效
        /// 通常是完全独立的，作为纯表现实体存在的特效
        /// 例如掉落物
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="path"></param>
        /// <param name="offset"></param>
        /// <returns></returns>
        public UniTask<Entity> ShowFreeEffect(Transform parent, string path, Vector3 offset = default)
        {
            return _entityEffectCtrl.ShowEntity(parent, path, offset);
        }

        /// <summary>
        /// 既不是区域特效/全局场景特效，又不是实体挂点特效
        /// 通常是完全独立的，作为纯表现实体存在的特效
        /// 例如掉落物
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="resCid"></param>
        /// <param name="offset"></param>
        /// <returns></returns>
        public UniTask<Entity> ShowFreeEffect(Transform parent, int resCid, Vector3 offset = default)
        {
            return _entityEffectCtrl.ShowEntity(parent, resCid, offset);
        }

        /// <summary>
        /// 服务器通知改变实体挂点特效
        /// </summary>
        /// <param name="singleEntityId"></param>
        /// <param name="multiEntityIds"></param>
        /// <param name="effectResCid"></param>
        /// <param name="bindPointCid"></param>
        /// <param name="isAdd"></param>
        public void OnChangeEntityBindEffect(int singleEntityId, List<int> multiEntityIds, int effectResCid, int bindPointCid, bool isAdd)
        {
            if (isAdd)
            {
                if (singleEntityId != 0 && multiEntityIds == null)
                {
                    PlayEffectOnEntity(singleEntityId, effectResCid, bindPointCid);
                }
                else if (multiEntityIds != null)
                {
                    foreach (var id in multiEntityIds)
                    {
                        PlayEffectOnEntity(id, effectResCid, bindPointCid);
                    }
                }
            }
            else
            {
                if (singleEntityId != 0 && multiEntityIds == null)
                {
                    HideEffectFromEntity(singleEntityId, effectResCid);
                }
                else if (multiEntityIds != null)
                {
                    foreach (var id in multiEntityIds)
                    {
                        HideEffectFromEntity(id, effectResCid);
                    }
                }
            }
        }

        private void PlayEffectOnEntity(int entityId, int effectResId, int bindPoint)
        {
            var entities = regionCtrl.entityCtrl.entitys;
            if (entities.TryGetValue(entityId, out var entity))
            {
                ShowEntityEffect(entity, (EntityBindPointCommonType)bindPoint, effectResId).Forget();
            }
            else
            {
                LogGame.LogError($"播放特效错误，找不到实体: {entityId}");
            }
        }

        /// <summary>
        /// 从实体身上移除特效（包括实体特效数据）
        /// 该特效属于策划配置的，需要存档的特效
        /// </summary>
        /// <param name="entityId"></param>
        /// <param name="effectResId"></param>
        private void HideEffectFromEntity(int entityId, int effectResId)
        {
            var entities = regionCtrl.entityCtrl.entitys;
            if (entities.TryGetValue(entityId, out var entity))
            {
                HideEntityEffect(entityId, effectResId);
                var dynEntity = entity.GetDynLevelEntity();
                dynEntity.ClearEffect();
            }
            else
            {
                LogGame.LogError($"停止特效错误，找不到实体: {entityId}");
            }
        }

        private void OnEntityStartHide(int entityId)
        {
            HideEntityEffect(entityId);
        }

        protected override void OnDispose()
        {
            MsgDispatcher.RemoveListener<int, List<int>, int, int, bool>(MsgEventType.Level_NotifyChangeBindEffect, OnChangeEntityBindEffect);
            MsgDispatcher.RemoveListener<int>(MsgEventType.Level_EntityStartHide, OnEntityStartHide);
            _effectMapping.Clear();
            _entityEffectMap.Clear();
        }
    }
}
